package com.yange.accountbook.net.api;

import static com.yange.accountbook.net.Api.BASE_URL;
import static com.yange.accountbook.net.observable.CommonObservable.schedulersTransformer;
import static com.yange.accountbook.net.observable.CommonObservable.transformer;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.trello.rxlifecycle2.android.ActivityEvent;
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity;
import com.yange.accountbook.entity.AccountEntity;
import com.yange.accountbook.entity.BooksEntity;
import com.yange.accountbook.entity.DeleteRecordPostEntity;
import com.yange.accountbook.net.TrustAllCerts;
import com.yange.accountbook.net.interceptor.LogInterceptor;
import com.yange.accountbook.utils.UserDao;

import org.json.JSONObject;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.X509TrustManager;

import io.reactivex.Observable;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;


/**
 * 网络请求库
 * 参考资料:
 * Retrofit:https://github.com/Tamicer/RetrofitClient
 * 泛型类型获取:https://www.cnblogs.com/linux007/p/5785046.html
 */

public class RetrofitClient {
    /*默认超时时间*/
    private static final int DEFAULT_TIMEOUT = 10;
    /*ApiService 接口类*/
    private ApiService apiService;
    /*OkHttpClient*/
    private OkHttpClient okHttpClient;
    /*baseurl  接口地址*/
    private static String baseUrl = BASE_URL;

    private Retrofit retrofit;

    private static class SingletonHolder {
        private static RetrofitClient INSTANCE = new RetrofitClient();
    }

    private RetrofitClient() {
        okHttpClient = new OkHttpClient.Builder()
                .sslSocketFactory(TrustAllCerts.createSSLSocketFactory(), new X509TrustManager() {

                    @Override
                    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                })
                .hostnameVerifier(new TrustAllCerts.TrustAllHostnameVerifier())
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .addNetworkInterceptor(
                        new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))//记录应用中的网络请求的信息
                .addInterceptor(new LogInterceptor())//日志拦截
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .build();
        /*创建Retrofit*/
        retrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(baseUrl)
                .build();
        /*创建ApiService 接口类*/
        apiService = retrofit.create(ApiService.class);
    }


    public static RetrofitClient getInstance() {
        return SingletonHolder.INSTANCE;
    }

    public ApiService getApiService() {
        return apiService;
    }

    /**
     * create you ApiService
     * Create an implementation of the API endpoints defined by the {@code service} interface.
     */
    public <T> T create(final Class<T> service) {
        if (service == null) {
            throw new RuntimeException("RecClient service is null!");
        }
        return retrofit.create(service);
    }

    /**
     * 上传图片
     *
     * @param mContext
     * @param path
     * @param file
     * @return
     */
    public Observable uploadPic(Context mContext, String path, File file) {
        Map<String, String> headers = new HashMap<>();
        RequestBody fileBody = RequestBody.create(MediaType.parse("image/jpg"), file);
        MultipartBody.Part body = null;
        try {
            body = MultipartBody.Part.createFormData("item", URLEncoder.encode(file.getName(), "UTF-8"), fileBody);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return apiService
                .uploadFile(path, headers, body)
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    /**
     * 上传图片(多张)
     *
     * @param mContext
     * @param path
     * @param
     * @return
     */
    public Observable uploadPics(Context mContext, String path, List<File> files) {
        Map<String, String> headers = new HashMap<>();
        MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        for (File file : files) {
            RequestBody requestBody = RequestBody.create(MediaType.parse("text/x-markdown; charset=utf-8"), file);
            try {
                builder.addFormDataPart("files", URLEncoder.encode(file.getName(), "UTF-8"), requestBody);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        List<MultipartBody.Part> parts = builder.build().parts();
        return apiService
                .uploadFiles(path, headers, parts)
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    /**
     * POST  body形式
     *
     * @param path
     * @param param
     * @return
     */
    public Observable invokePostBody(Context mContext, String path, Map<String, Object> param) {
        Map<String, String> headers = new HashMap<>();
        JSONObject json;
        if (param != null) {
            json = new JSONObject(param);
        } else {
            json = new JSONObject(new HashMap());
        }
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        RequestBody body = RequestBody.create(JSON, json.toString());
        Log.i("yange-post", path + json.toString());
        return apiService
                .invokePost(path, headers, body)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }


    public Observable invokePostBodyNoToken(Context mContext, String path, Map<String, Object> param) {
        Map<String, String> headers = new HashMap<>();
        JSONObject json;
        if (param != null) {
            json = new JSONObject(param);
        } else {
            json = new JSONObject(new HashMap());
        }
        Log.i("yange-post", path + json.toString());
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(JSON, json.toString());
        return apiService
                .invokePost(path, headers, body)
//                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }


    /**
     * post 请求 表单形式
     * content 先传进来 后续可能会把请求和生命周期绑定起来
     *
     * @param path
     * @param param
     * @return
     */
    public Observable invokePost(Context mContext, String path, Map<String, Object> param) {
        Map<String, String> headers = new HashMap<>();
        if (param == null) {
            param = new HashMap<>();
        }
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        return apiService
                .invokePost2(path, headers, param)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    public Observable deleteRecord(Context mContext, String path, List<Integer> array) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        DeleteRecordPostEntity recordPost = new DeleteRecordPostEntity();
        recordPost.addAll(array);
        return apiService
                .deleteRecordOrder(path, headers, recordPost)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }


    /**
     * 账本排序
     * @param mContext
     * @param path
     * @param array
     * @return
     */
    public Observable bookSort(Context mContext, String path, ArrayList<BooksEntity> array) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        return apiService
                .bookSort(path, headers, array)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    public Observable accountSort(Context mContext, String path, ArrayList<AccountEntity> array) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        return apiService
                .accountSort(path, headers, array)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    public Observable feeItemSort(Context mContext, String path, List<Integer> array) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        return apiService
                .sort(path, headers, array)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    public Observable invokeGetNoToken(Context mContext, String path, Map<String, String> param) {
        if (param == null) {
            param = new HashMap<>();
        }
        return apiService
                .invokeGet(path, param)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    /**
     * get请求 带token
     *
     * @param path
     * @param param
     * @return
     */
    public Observable invokeGet(Context mContext, String path, Map<String, Object> param) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        return apiService
                .invokeGet(path, headers, param)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    public Observable invokeGet(Context mContext, String path) {
        return invokeGet(mContext, path, new HashMap<>());
    }


    /**
     * 获取账户详情
     *
     * @param mContext
     * @param id
     * @return
     */
    public Observable invokeGetBookInfo(Context mContext, int id) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        return apiService
                .invokeGetBookInfo(headers, id)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }


    /**
     * 获取图标
     * @param mContext
     * @param type
     * @return
     */
    public Observable invokeGetIconList(Context mContext, String type) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        return apiService
                .invokeGetIconList(headers, type)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }


    /**
     * 获取账户详情
     *
     * @param mContext
     * @param id
     * @return
     */
    public Observable invokeGetAccountInfo(Context mContext, int id) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        return apiService
                .invokeGetAccountInfo(headers, id)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }


    /**
     * 通过账本码添加
     * @param mContext
     * @param code
     * @return
     */
    public Observable addBookByCode(Context mContext, String code) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        return apiService
                .addBookByCode(headers, code)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    /**
     * 设置账期
     *
     * @param mContext
     * @return
     */
    public Observable setDay(Context mContext, int day) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        Log.i("yange-post", day + "");
        return apiService
                .setDay(headers, day)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }

    public Observable setPeriodStatus(Context mContext, boolean enable) {
        Map<String, String> headers = new HashMap<>();
        if (!TextUtils.isEmpty(UserDao.INSTANCE.getToken())) {
            headers.put("token", UserDao.INSTANCE.getToken());
        }
        Log.i("yange-post", enable + "");
        return apiService
                .setPeriodStatus(headers, enable)
                .compose(((RxAppCompatActivity) mContext).bindUntilEvent(ActivityEvent.DESTROY))
                .compose(schedulersTransformer())
                .compose(transformer());

    }



}
